package com.github.niefy.qrcode;

import com.github.niefy.modules.oss.cloud.OSSFactory;
import com.google.common.base.Charsets;
import com.google.common.collect.Maps;
import com.google.zxing.*;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.HybridBinarizer;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import org.apache.commons.io.FileUtils;
import org.apache.http.Consts;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.apache.http.client.HttpResponseException;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileChannel.MapMode;
import java.nio.file.Files;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;

@Component
public class AddressCodeUtils {

	private static final String FORMAT = "png";// 生成二维码的格式
	private static String bgColor;

	private static Logger logger = LoggerFactory.getLogger(AddressCodeUtils.class);

	/**
	 * 根据内容生成二维码数据
	 *
	 * @param content
	 *            二维码文字内容[为了信息安全性，一般都要先进行数据加密]
	 * @param length
	 *            二维码图片宽度和高度
	 */
	private static BitMatrix createQrcodeMatrix(String content, int length) {
		Map<EncodeHintType, Object> hints = Maps.newEnumMap(EncodeHintType.class);
		// 设置字符编码
		hints.put(EncodeHintType.CHARACTER_SET, Charsets.UTF_8.name());
		// 指定纠错等级
		hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.L);
		hints.put(EncodeHintType.MARGIN, 1);
		System.out.println(content+BarcodeFormat.QR_CODE+length+length+hints);
		try {
			return new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, length, length, hints);
		} catch (Exception e) {
			logger.warn("内容为：【" + content +"】的二维码生成失败！", e);
			return null;
		}
	}

	/**
	 * 根据指定边长创建生成带背景的二维码
	 *
	 * @param content
	 *            二维码内容
	 * @param length
	 *            二维码的高度和宽度
	 * @param logoFile
	 *            logo 文件对象，可以为空
	 * @return 二维码图片的字节数组
	 */
	public static String createQrcode(MatrixToBgImageConfig config) {
		try {
			InputStream inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(config.getBgFile());
			File bgFile = Files.createTempFile("bg_", ".png").toFile();
			FileUtils.copyInputStreamToFile(inputStream, bgFile);

			logger.info("背景图 {}", bgFile);

			if (bgFile != null && !bgFile.exists()) {
				throw new IllegalArgumentException("请提供正确的背景文件！");
			}
			BitMatrix qrCodeMatrix = createQrcodeMatrix(config.getQrcode_url(), config.getQrcode_height());
			if (qrCodeMatrix == null) {
				throw new IllegalArgumentException("请提供正确的二维码图片地址");
			}
			File file = Files.createTempFile("qrcode_" + UUID.randomUUID(), "." + FORMAT).toFile();
			logger.debug(file.getAbsolutePath());
			MatrixToImageWriter.writeToFile(qrCodeMatrix, FORMAT, file);
			if (bgFile != null) {
				// 添加背景图片, 此处一定需要重新进行读取，而不能直接使用二维码的BufferedImage 对象
				BufferedImage img = ImageIO.read(file);
				increasingImage(img, FORMAT, file.getAbsolutePath(), bgFile, config);
				bgFile.delete();
			}

		try{
			//FileUtils.moveFile(file,new File("d://"+config.getDevice_code()+"_"+config.getQrcode_size()+".png"));
//			FileUtils.moveFile(file,new File(config.getDevice_code()+"_"+bgColor+".png"));

			//上传文件
			String suffix = Objects.requireNonNull(file.getName()).substring(file.getName().lastIndexOf("."));
			String url = Objects.requireNonNull(OSSFactory.build()).uploadSuffix(toByteArray(file), suffix);

			File oldfile= new File(file.getAbsolutePath());
			oldfile.deleteOnExit();
			return url;
		    }catch(Exception e){
		    	logger.warn("二维码重复");
		    	file.deleteOnExit();
		    	return null;
		    }
		} catch (Exception e) {
			logger.warn("内容为：【" + config.getQrcode_url() + "】的二维码生成失败！", e);
			return null;
		}
	}

	/**
	 * 将文件转换为字节数组， 使用MappedByteBuffer，可以在处理大文件时，提升性能
	 *
	 * @param file
	 *            文件
	 * @return 二维码图片的字节数组
	 */
	private static byte[] toByteArray(File file) {
		try (FileChannel fc = new RandomAccessFile(file, "r").getChannel();) {
			MappedByteBuffer byteBuffer = fc.map(MapMode.READ_ONLY, 0, fc.size()).load();
			byte[] result = new byte[(int) fc.size()];
			if (byteBuffer.remaining() > 0) {
				byteBuffer.get(result, 0, byteBuffer.remaining());
			}
			return result;
		} catch (Exception e) {
			logger.warn("文件转换成byte[]发生异常！", e);
			return null;
		}
	}

	private static void increasingImage(BufferedImage image, String format, String imagePath, File bgFile,
			MatrixToBgImageConfig config) throws Exception {
		try {
			//BufferedImage bg = ImageIO.read(bgFile);

			Image src = Toolkit.getDefaultToolkit().getImage(bgFile.getPath());
			BufferedImage bg = toBufferedImage(src);

			Graphics2D g = bg.createGraphics();

			g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB);

			// 二维码的高度和宽度如何定义
			int width = config.getQrcode_height();
			int height = config.getQrcode_height();

			// 二维码起始位置
			int x = config.getQrcode_x();
			int y = config.getQrcode_y();
			// 绘制图
			g.drawImage(image, x, y, width, height, null);

			// 绘制文字
			g.setColor(Color.BLACK);// 文字颜色
			Font font = new Font("simhei", Font.BOLD, config.getFontSize());
			g.setFont(font);


			g.drawString(config.getDevice_code(), config.getDevice_code_x(), config.getDevice_code_y());

			g.dispose();
			// 写入二维码到bg图片
			ImageIO.write(bg, format, new File(imagePath));
		} catch (Exception e) {
			throw new Exception("二维码添加bg时发生异常！", e);
		}
	}

	/**
	 * 解析二维码
	 *
	 * @param file
	 *            二维码文件内容
	 * @return 二维码的内容
	 */
	public static String decodeQrcode(File file) throws IOException, NotFoundException {
		BufferedImage image = ImageIO.read(file);
		LuminanceSource source = new BufferedImageLuminanceSource(image);
		Binarizer binarizer = new HybridBinarizer(source);
		BinaryBitmap binaryBitmap = new BinaryBitmap(binarizer);
		Map<DecodeHintType, Object> hints = Maps.newEnumMap(DecodeHintType.class);
		hints.put(DecodeHintType.CHARACTER_SET, Charsets.UTF_8.name());
		return new MultiFormatReader().decode(binaryBitmap, hints).getText();
	}

	public static InputStream handleResponse(final HttpResponse response) throws IOException {
		final StatusLine statusLine = response.getStatusLine();
		final HttpEntity entity = response.getEntity();
		if (statusLine.getStatusCode() >= 300) {
			EntityUtils.consume(entity);
			throw new HttpResponseException(statusLine.getStatusCode(), statusLine.getReasonPhrase());
		}
		return entity == null ? null : entity.getContent();
	}

	public static String handleUTF8Response(final HttpResponse response) throws IOException {
		final StatusLine statusLine = response.getStatusLine();
		final HttpEntity entity = response.getEntity();
		if (statusLine.getStatusCode() >= 300) {
			EntityUtils.consume(entity);
			throw new HttpResponseException(statusLine.getStatusCode(), statusLine.getReasonPhrase());
		}
		return entity == null ? null : EntityUtils.toString(entity, Consts.UTF_8);
	}

	public static File createTmpFile(InputStream inputStream, String name, String ext) throws IOException {
		System.out.println("inputStream:"+inputStream+",name:"+name+",ext:"+ext);
		File tmpFile = File.createTempFile(name, '.' + ext);

		tmpFile.deleteOnExit();

		try (FileOutputStream fos = new FileOutputStream(tmpFile)) {
			int read = 0;
			byte[] bytes = new byte[1024 * 100];
			while ((read = inputStream.read(bytes)) != -1) {
				fos.write(bytes, 0, read);
			}

			fos.flush();
			return tmpFile;
		}
	}

	/**
	 * 将图片转成BufferedImage格式
	 *
	 **/
	private static BufferedImage toBufferedImage(Image image) {
		if (image instanceof BufferedImage) {
			return (BufferedImage) image;
		}
		// This code ensures that all the pixels in the image are loaded
		image = new ImageIcon(image).getImage();
		BufferedImage bimage = null;
		GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
		try {
			int transparency = Transparency.OPAQUE;
			GraphicsDevice gs = ge.getDefaultScreenDevice();
			GraphicsConfiguration gc = gs.getDefaultConfiguration();
			bimage = gc.createCompatibleImage(image.getWidth(null),
					image.getHeight(null), transparency);
		} catch (HeadlessException e) {
			// The system does not have a screen
		}
		if (bimage == null) {
			// Create a buffered image using the default color model
			int type = BufferedImage.TYPE_INT_RGB;
			bimage = new BufferedImage(image.getWidth(null), image.getHeight(null), type);
		}
		// Copy image to buffered image
		Graphics2D g = bimage.createGraphics();

		// 增加透明度解决png透明图片会变黑的问题
		bimage = g.getDeviceConfiguration().createCompatibleImage(image.getWidth(null), image.getHeight(null), Transparency.TRANSLUCENT);
		g = bimage.createGraphics();

		// Paint the image onto the buffered image
		g.drawImage(image, 0, 0, null);
		g.dispose();
		return bimage;
	}

	/**
	 * 拼接设备编号
	 * @param format 图片类型
	 * @param bgFile 背景图片
	 * @param config 拼接属性
	 * @throws Exception
	 */
	private static void increasingDeviceCode(MatrixToBgImageConfig config,Color c) throws Exception {
		try {
			InputStream inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(config.getBgFile());
			File bgFile = Files.createTempFile("bg_", ".png").toFile();
			FileUtils.copyInputStreamToFile(inputStream, bgFile);
			logger.info("背景图 {}", bgFile);
			Image src = Toolkit.getDefaultToolkit().getImage(bgFile.getPath());
			BufferedImage bg = toBufferedImage(src);

			Graphics2D g = bg.createGraphics();

			g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB);

			// 绘制文字

			g.setColor(c);// 文字颜色
			Font font = new Font("simhei", Font.PLAIN, config.getFontSize());
			g.setFont(font);

			g.drawString(config.getDevice_code(), config.getDevice_code_x(), config.getDevice_code_y());

			g.dispose();
			File File = Files.createTempFile("", ".png").toFile();
			// 写入二维码到bg图片
			ImageIO.write(bg, FORMAT, new File(File.getAbsolutePath()));
			FileUtils.moveFile(File,new File("upload/qrcode/"+config.getDevice_code()+"_"+config.getQrcode_size()+".png"));
			File.delete();
		} catch (Exception e) {
			throw new Exception("二维码添加bg时发生异常！", e);
		}
	}

}
